package bgai.gamelogic;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import bgai.ai.minimax.MoveEvent;

/**
 * Definisce una mossa compiuta in Backgammon.
 * <p>
 * Una mossa e' definita come una sequenza NON ordinata di mosse di checker.
 * <p>
 * Non viene fatto alcun tipo di controllo sulle mosse
 * @see CheckerMove
 * @see SingleMove
 */
public class Move implements Iterable<SingleMove>, MoveEvent, Cloneable {
	
	/**
	 * Dado di valore piu' elevato
	 */
	private int diceHigh;
	/**
	 * Dado di valore meno elevato
	 */
	private int diceLow;
	/**
	 * Colore del giocatore che ha mosso
	 */
	private CheckerColor color;
	
	/**
	 * Lista non ordinata delle mosse dei checker
	 */
	private Vector<SingleMove> moves;
	
	/**
	 * Crea una mossa vuota. Non c'e' ordinamento fra i dadi.
	 * @param dice1 un dado
	 * @param dice2 l'altro dado
	 * @param color colore del giocatore che ha mosso
	 * @throws DiceException se i dadi non sono validi
	 */
	public Move(int dice1, int dice2, CheckerColor color) throws DiceException {
		
		if (!Backgammon.checkDice(dice1, dice2)) {
			throw new DiceException(dice1, dice2, "Dadi non validi");
		}
		
		if (dice1>dice2) {
			this.diceHigh = dice1;
			this.diceLow = dice2;
		} else {
			this.diceHigh = dice2;
			this.diceLow = dice1;
		}
		this.color = color;
		this.moves = new Vector<SingleMove>(); // di default ha dimensione 10
	}
	
	/**
	 * Aggiunge la mossa di un checker, se la mossa e' valida
	 * @param move
	 * @throws BadMoveException mossa non valida
	 */
	public void add(CheckerMove move) throws BadMoveException {
		if (!color.equals(move.getColor())) {
			throw new BadMoveException("Stai aggiungendo ad un Move un CheckerMove di colore sbagliato");
		}

		for (SingleMove m : move) {
			moves.add(m);
		}
		// importante: ordinare
		Collections.sort(moves);
	}
	
	/**
	 * Aggiunge la mossa di un checker, se la mossa e' valida
	 * @param move
	 * @throws BadMoveException mossa non valida
	 */
	public void add(SingleMove move) throws BadMoveException {
		if (!color.equals(move.getColor())) {
			throw new BadMoveException("Stai aggiungendo ad un Move un CheckerMove di colore sbagliato");
		}

		moves.add(move);
		
		// importante: ordinare
		Collections.sort(moves);
	}
	
	public void clearHit() {
		for (SingleMove sm : moves) {
			sm.setHit(false);
		}
	}

	/**
	 * Restituisce il numero di CheckerMove in questa mossa
	 * @return
	 */
	public int size() {
		return moves.size();
	}
	
	/**
	 * Restituisce il dado di valore piu' elevato
	 * @return
	 */
	public int getHighDice() {
		return diceHigh;
	}

	/**
	 * Restituisce il dado di valore meno elevato
	 * @return
	 */
	public int getLowDice() {
		return diceLow;
	}

	/**
	 * Restituisce la lista non ordinata dei dadi usati per questa mossa
	 * @return
	 */
	public List<Integer> getUsedDices() {
		List<Integer> list= new LinkedList<Integer>();
		for (SingleMove m : moves) {
			list.add(m.getDice());
		}
		return list;
	}
	
	/**
	 * Restituisce il colore del giocatore che ha effettuato questa mossa
	 * @return
	 */
	public CheckerColor getColor() {
		return color;
	}

	@Override
	public Iterator<SingleMove> iterator() {
		return moves.iterator();
	}
	
	@Override
	public String toString() {
		String str= ""+color+" "+diceHigh+"-"+diceLow+":";
		for (SingleMove m : this) {
			str += " " + m;
		}
		return str; 
	}
	
	public String rawToString() {
		String str= ""+color+" "+diceHigh+"-"+diceLow+":";
		
		String str2= "[";
		boolean first= true;
		for (SingleMove cm : moves) {
			if (first) {
				str2+= cm.rawToString();
				first= false;
			} else {
				str2+= ", " + cm.rawToString();
			}
		}
		str+= str2+"]";
		
		return str;
	}
	
	
	@Override
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof Move) {
        	Move mv = (Move) anObject;
        	
        	if (this.diceHigh != mv.diceHigh) return false;
        	if (this.diceLow != mv.diceLow) return false;
        	if (! this.color.equals(mv.color) ) return false;
        	if (! this.moves.equals(mv.moves)) return false;
        	
        	// tutto uguale
        	return true;
        }
        return false;
    }

	@Override
	public Object clone() {
		try {

			Move clone = (Move) super.clone();
			
			// l'unica cosa da fare e' clonare la lista
			clone.moves= new Vector<SingleMove>();
			for (SingleMove move : moves) {
				clone.moves.add( (SingleMove) move.clone());
			}
			
			return clone;
			
		} catch (CloneNotSupportedException e) {
			// non accadra' mai
			throw new InternalError("Come puo' essere CloneNotSupportedException?");
		}
	}

	@Override
	public int hashCode() {
		int hash= 1;
		
		hash = 7 * hash + color.hashCode();
		hash = 13 * hash + diceHigh;
		hash = 17 * hash + diceLow;
		
		/*
        int h = 0;
        for (CheckerMove cm : moves) {
        	if (cm != null) {
        		int h2= cm.hashCode();
        		h += h2;
        	}
        }
		hash = 31 * hash + h;
		*/
		hash = 31 * hash + moves.hashCode();
		
		return hash;
	}

}

